#include <8051.h>

/*
theory and notes
OHC = overhead code

Since there is some overhead to read in the duty cycle, a possible way to correct this is
to start a timer before the overhead code(OHC) and stop it afterwards, then initialize and preset the loop to
some time t - a, where a is the # ticks of the timer and t is the time if there was no over head code. The expected
result is such that the time that is spent high(after the for loop) is equaled to a time spent low
by the predelay of t - a, effectly the wave form will be symmetric.

*/


//--------Globals-----------------------------------
//packet structure
//byte:  0       1      2       3       4      5        6            
//     ['F'][Ch0Duty][Ch1Duty][(OE)][Sol12V][Sol24V][Checksum]
unsigned char packet[7];
unsigned char index;
volatile unsigned char frame_started; //used by serial ISR to remember if a frame has started(based on identified char 'F'
volatile unsigned char frame_complete; //use by serial ISR and main() to indicate that a frame is complete and ready for processing
//main() should clear frame complete when it is done processing

unsigned char uart_data;

unsigned char timer0_of;

#define ch0 P3_7
#define ch1 P3_5
#define nOE P1_1  //active low controlling output FET gates
#define select P3_4

#define LED P1_2
#define LED0 P1_3
#define LED1 P1_4
#define TIME P1_5

//RS232 commands
#define OE_ACTIVE 0xAA //used in pack[3] to indicate
#define INTERFACE_ACTIVE 0xAF
#define REQ_PACK_ISR 's' //RS232 CMD request port states (occurs from interrupt)
#define REQ_PACK 'r' //same as above but occurred from main loop

#define cycle_ticks 3333

//-------------------------------------------------------




//'------------I2C functions----------------
/*
#define SDA P1_7
#define SCL P1_6

//bits fir slave_addr [0][1][1][1][a2][a1][a0][w/r]
unsigned char i2c_read(unsigned char slave_addr);
void i2c_write(unsigned char slave_addr, unsigned char byte);

unsigned char i2c_tx(unsigned char byte);
unsigned char i2c_rx(unsigned char ack);

void i2c_start();
void i2c_stop();
*/
//-----------------------------------------

//---------Serial Functions ------------------------

//#pragma callee_saves InitSerial

#pragma callee_saves Tx

void InitSerial(); //initialize for serial I/O

void Tx(char b); //send byte via serial port
char Rx(); //receive byte

void InitSerial()
{
       // TR1 = 0;
       //TCON = 0;
        TR1 = 0; //disable timer before configuring mode
        TF1 = 0;//maybe necessary
       
        TI = 0;
        RI = 0;
       
        PCON = 0x80; //double this baudrate (SMOD = 1)
        SCON = 0x50; //select mode, enable reception
        //TH1 = 221; //timer overflow reg
        //TH1 = baud;//115200 bps @ 22.1184MHzx2, 1337
        TH1 = 255; //57600 bps @ 11.0592 (SMOD = 1)        
        //TH1 = 182 at 3.120MHz;
        
        TMOD = (TMOD & 0x0F) | 0x20; //set timer #1 for 8 bit auto-reload; lower nible timer0 config'd as 16-bit timer
        
        TR1 = 1; //bit 6 of TCON to enable timer 1;  
        return;
}

void Tx(char b)
{
   TI = 0;
  // SBUF = b;
   _asm mov _SBUF, dpl _endasm;
   while(!TI);
   return;
}

char Rx()
{
   RI = 0;
   while(!RI);
  // RI = 0;
   return SBUF;
}

//-----------------------interrupt serial

void serial_isr(void) interrupt 4
{
     /*
     If the interrupt service routine is defined without using a register bank or with register
bank 0 (using 0), the compiler will save the registers used by itself on the stack upon
entry and restore them at exit, however if such an interrupt service routine calls another
function then the entire register bank will be saved on the stack. This scheme may be
advantageous for small interrupt service routines which have low register usage.
If the interrupt service routine is defined to be using a specific register bank then
only a, b & dptr are save and restored, if such an interrupt service routine calls another
function (using another register bank) then the entire register bank of the called function
will be saved on the stack. This scheme is recommended for larger interrupt service
routines.
Calling other functions from an interrupt service routine is not recommended
*/
     
     if (RI == 1)
     {
            RI = 0; // clear reception flag for next reception 
            uart_data = SBUF; 
           // SBUF = uart_data;  //Send back same data on uart
            

            if(!frame_complete)
            {
                if(frame_started) {
                     packet[index] = uart_data;              
                      
                      if(index == 6)
                      {
                        frame_complete = 1; //flag that a frame has completed and ready for processing by main() 
                        frame_started = 0; //reset for next transmission
                        LED = !LED;
                        //index = 0;
                      } else {
                       index++;
                      }
                    
                } else if(uart_data == 'F') { //start character receceived
                     frame_started = 1;
                     index = 1;
                     packet[0] = uart_data;
                }
            }
      } 
      
    //  if(TI == 1) TI = 0; //clear transmission flag for next transmission 
     
      //Tx(0xff); 
      return;
}  

void timer0_isr() interrupt 1 //_naked
{
   timer0_of++;
  //__asm reti __endasm;
  // ES = 0;
   if(timer0_of == 99)
   {
       timer0_of = 0;
       
     //  LED0 = !LED0;
       nOE = 1; //disable outputs
      
       TI = 0;
       SBUF = REQ_PACK_ISR;
    }
  
  // ES = 1;
   
  //return;
} 
//----------------------------
void main(void)
{
  //   typedef uc unsigned char;
     
     unsigned char ch0duty, ch1duty, i;
     unsigned char checksum;
     
     ch0duty = 0;
     ch1duty = 0;
     
     index = 0;
     frame_started = 0;
     frame_complete = 0;
     nOE = 1;
     
     timer0_of = 0;
     
     InitSerial();
    // TMOD &= 0xF1; //sset timer0 as 16 bit
   //  REN = 1; //SCON, enable serial reception
     ES = 1; // Enable serial interrupt
     ET0 = 1; //IE.1 timer0 interrupt
     EA = 1; // Enable global interrupt
     
     TI = 0;
     SBUF = INTERFACE_ACTIVE; //send interface active
      
     while(1)
     {
          frame_complete = 0;
  
     //read in duty cycle from port P1(assumes multiplexed)
      //OHC start    
          //select = 0; //select first 
          //ch0duty = P1;
          //select = 1; //select  second
          //ch1duty = P1;
          TR0 = 1; //enable timer0 for 'pseudowatch dog'
          
          TI = 0;
          SBUF = REQ_PACK; // r for request
          while(!frame_complete);// Tx(0x33);

         // TIME =
          TR0 = 0; //disable timer 0
          
          //Tx(TH0);
          //Tx(TL0);
          
            //  if(frame_complete)
              //{
                      //calculate checksum 
                      checksum = packet[1] + packet[2] + packet[3] + packet[4] + packet[5];
                    
                      if(checksum != packet[6]) //checksum error
                      {
                         //frame_flag = 0; 
                         LED1 = !LED1;
                         
                         TI = 0;
                         SBUF = 0xA1;
                         while(!TI);
                         
                        // TI = 0;
                        // SBUF = checksum;
                       //  while(!TI);
                         
                         continue;
                               
                      } else { //valid data received, process
                          if(packet[3] == OE_ACTIVE) nOE = 0;// OE is a GO
                            else nOE = 1;
                          
                          ch0duty = packet[1];
                          ch1duty = packet[2];
                       
                        //  i2c_write(0x70, packet[4]); //12V relay IOs
                        //  i2c_write(0x72, packet[5]); //24V relay IOs
    
                          //i2c_write(0, ch0duty);
    
                      }
                      
                     // frame_complete = 0; //flag that the frame has been processed to serial ISR
                   //frame_started = 0; this code is unneeded since this is clear the moment a frame is complete in serialISR()
             // }
          
          if(ch0duty != 255)
             ch0 = 0;
          
          if(ch1duty != 255)
             ch1 = 0;
       //OHC end
          
          for(i = 0; i < 255; i++) //complete one cycle
          {
              if((255 - ch0duty) == i)
                ch0 = 1;
                
              if((255 - ch1duty) == i)
                ch1 = 1;
                
          }
          
            
     }
}

/*


//-------------------------------------------------------------------
unsigned char i2c_read(unsigned char slave_addr)
{
   unsigned char byte;
   
   i2c_start();
   i2c_tx(slave_addr | 1); //lower bit indicates read operation
   
   byte = i2c_rx(1); //assert a master ack
   i2c_stop();
   return byte;
}


void i2c_write(unsigned char slave_addr, unsigned char byte)
{
  // unsigned char byte;
   
   i2c_start();
  
   i2c_tx(slave_addr & 0xFE); //lower bit indicates \write operation
   
   i2c_tx(byte);
   
   i2c_stop();
     return;
}
void i2c_start()
{
   //START condition; SDA H to L while SCL = 1
  //Note: this is also a stop condition if SDA is low before SCL = 1!
  //Bus assumed idle when SDA and SCL are high?
    SDA = 1;             // i2c start bit sequence
    SCL = 1;
    SDA = 0;
    SCL = 0;
}

void i2c_stop()
{
    //STOP condition; SDA from L to H while SCL = 1 
    SDA = 0;  
    SCL = 1; 
    
    SDA = 1; 
}

//shift out one byte MSB first
unsigned char i2c_tx(unsigned char byte) 
{   
  unsigned char i;
  static bit ackbit; //static necessary?

  for(i = 0x80; i; i >>= 1)
  {
      if (byte & i) 
        SDA = 1;
      else 
        SDA = 0;

    SCL = 1;
    SCL = 0; //changing of SDA only allowed when SCL = 0
  } 
    
    SDA = 1;
    
    SCL = 1; //slave device now pulling SDA low for acknowledgement
    
    ackbit = SDA;
     
    SCL = 0;
    
    return ackbit;
}

unsigned char i2c_rx(unsigned char ack)
{
     unsigned char byte = 0, i;
     
   //  SCL = 0; //if SCL = 0 is not used in i2c_tx(after a tx, either RX, another tx, or stop is called)
     
     SDA = 1;
     
     for (i = 0x80; i; i >>=1)
     {
       SCL = 1;
       
       while(!SCL);
       
       if (SDA) byte |= i; 
       SCL = 0;  
     }
     
     //Assert SDA(ack from master)
    if(ack) SDA = 0; //assert SDA low while SCL is high?
    else SDA = 1;
    
    SCL = 1; //clock ack bit
    SCL = 0;
    
  //  SDA = 1; //necessary??
     return byte;
}
*/
